import artTemplate from "art-template"
import dayjs from "dayjs"
import _ from "lodash"
import {JSONPath} from "jsonpath-plus"

export const defaultData = {
  json: `{
    "tableComment": "用户表",
    "tableName": "sys_user",
    "columns": [
      {
        "columnComment": "主键",
        "columnName": "id",
        "columnType": "varchar(64)",
        "isPrimary": true,
        "isAutoIncrement": false,
        "isNonNull": true,
        "columnDefault": null
      },
      {
        "columnComment": "账号",
        "columnName": "username",
        "columnType": "varchar(64)",
        "isPrimary": false,
        "isAutoIncrement": false,
        "isNonNull": true,
        "columnDefault": null
      },
      {
        "columnComment": "密码",
        "columnName": "password",
        "columnType": "varchar(256)",
        "isPrimary": false,
        "isAutoIncrement": false,
        "isNonNull": true,
        "columnDefault": null
      },
      {
        "columnComment": "姓名",
        "columnName": "name",
        "columnType": "varchar(64)",
        "isPrimary": false,
        "isAutoIncrement": false,
        "isNonNull": false,
        "columnDefault": null
      },
      {
        "columnComment": "手机号",
        "columnName": "phone",
        "columnType": "varchar(64)",
        "isPrimary": null,
        "isAutoIncrement": false,
        "isNonNull": false,
        "columnDefault": null
      },
      {
        "columnComment": "邮箱",
        "columnName": "email",
        "columnType": "varchar(64)",
        "isPrimary": null,
        "isAutoIncrement": false,
        "isNonNull": false,
        "columnDefault": null
      },
      {
        "columnComment": "登录错误次数",
        "columnName": "err_count",
        "columnType": "int",
        "isPrimary": false,
        "isAutoIncrement": false,
        "isNonNull": false,
        "columnDefault": null
      }
    ]
  }`.trim(),
  funcName: 'Js引擎',
  funcOptions: [
    {
      name: 'Js引擎',
      link: 'https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array',
      code: `
return this
      
/*
原理: new Function(代码).call(数据)
示例: this.columns
          .filter(c => c.columnType.startsWith('varchar'))
          .map(c => c.columnName)
*/

      `.trim(),
      extCode: ''
    },
    {
      name: 'JsonPath',
      link: 'https://goessner.net/articles/JsonPath/index.html#e2',
      code: `
$.*

/*
原理: JSONPath({path: 代码, json: 数据})
示例: $.columns[0:2]
说明: 
  $                  根对象或元素.
  @                  当前对象或元素.
  . or []            子元素操作符.
  ..                 递归匹配所有子元素.
  *                  通配符. 匹配所有对象或元素.
  []                 下标运算符，JsonPath索引从0开始.
  [,]                连接运算符，将多个结果拼成数组返回，JSONPath允许使用别名.
  [start:end:step]   数组切片运算符.
  ?()                过滤器（脚本）表达式.
  ()                 脚本表达式.

帮助: 
  https://goessner.net/articles/JsonPath/index.html#e2
  https://github.com/JSONPath-Plus/JSONPath#syntax-through-examples
*/
      `.trim(),
      extCode: ''
    },
    {
      name: 'Art模板',
      link: 'https://goofychris.github.io/art-template/zh-cn/docs/',
      code: `
<%
// 可选择右侧示例模板作为参考; 为方便使用已添加部分通用函数
// Art模板手册: https://goofychris.github.io/art-template/zh-cn/docs/
%>
    `.trim(),
      extCode: `
<% 
// 模板语法: https://goofychris.github.io/art-template/zh-cn/docs/index.html

// 已引入的常用函数: dayjs, lodash(_), 可直接使用，文档参考如下
// 日期处理: dayjs   https://dayjs.fenxianglu.cn/category
// 常用函数：lodash  https://www.lodashjs.com

// 转驼峰
function camel(str) {
  return str?.replace(/_(\\\\w)/g, (_, p1) => p1.toUpperCase())
}

// 帕斯卡命名（大驼峰）
function pascal(str) {
  const newStr = camel(str)
  return newStr ? newStr.charAt(0).toUpperCase() + newStr.slice(1) : newStr;
}

// 下划线
function snake(str) {
  return str?.replace(/([A-Z])/g, '_$1').toLowerCase();
}

/* 类型映射 */
function typeMapping(columnType, lang) {
  // 为了能够将函数写到模板的下面，定义在函数里面了
  const typeMappingData = [
    {id: 'sys01', name: '字符串', code: 'STRING'  , color: '#409eff', enabled: true, mysql: 'VARCHAR' , oracle: 'VARCHAR2', postgres: 'VARCHAR'  , java: 'String' , rust: 'String'         , go: 'string'     , sourceTypes: 'STRING, VARCHAR, VARCHAR2, CHARACTER VARYING, CHAR, CHARACTER'},
    {id: 'sys02', name: '整数'  , code: 'INT'     , color: '#67c23a', enabled: true, mysql: 'INT'     , oracle: 'INT'     , postgres: 'INT'      , java: 'Integer', rust: 'i32'            , go: '*int'       , sourceTypes: 'INT, INTEGER, SERIAL'},
    {id: 'sys03', name: '小数'  , code: 'DECIMAL' , color: '#e6a23c', enabled: true, mysql: 'DECIMAL' , oracle: 'DECIMAL' , postgres: 'DECIMAL'  , java: 'Double' , rust: 'f64'            , go: '*float64'   , sourceTypes: 'DECIMAL, NUMERIC, REAL, DOUBLE'},
    {id: 'sys04', name: '日期'  , code: 'DATETIME', color: '#909399', enabled: true, mysql: 'DATETIME', oracle: 'DATE'    , postgres: 'TIMESTAMP', java: 'Date'   , rust: 'DateTime<Local>', go: '*time.Time' , sourceTypes: 'DATE, DATETIME, TIMESTAMP'},
    {id: 'sys05', name: '大文本', code: 'TEXT'    , color: '#f56c6c', enabled: true, mysql: 'TEXT'    , oracle: 'CLOB'    , postgres: 'TEXT'     , java: 'String' , rust: 'String'         , go: 'string'     , sourceTypes: 'TEXT, CLOB'},
    {id: 'sys06', name: '二进制', code: 'BLOB'    , color: '#f56c6c', enabled: true, mysql: 'BLOB'    , oracle: 'BLOB'    , postgres: 'BYTEA'    , java: 'byte[]' , rust: 'Vec<u8>'        , go: '[]byte'     , sourceTypes: 'BLOB, BYTEA'},
  ]

  // varchar(20) ==> VARCHAR
  const simpleType = columnType?.split('(')[0]?.toUpperCase()
  for (let type of typeMappingData) {
    const arr = type.sourceTypes.split(',').map(t => t.trim())
    if (arr.includes(simpleType)) {
      return type[lang]
    }
  }
  return columnType
}
%>
`.trim()
    }
  ],

  tmpl: null,
  tmplOptions: [
    {
      name: 'go',
      lang: 'go',
      code: `
package models
<%
// 去掉sys_前缀
const modelName = pascal(tableName.split('_')[1])  

// 最大字段长度和类型长度，用于对齐
const maxFieldLength = columns.map(c => pascal(c.columnName).length).reduce( (p,n) => p > n ? p : n)
const maxTypeLength  = columns.map(c => typeMapping(c.columnType, 'go').length).reduce( (p,n) => p > n ? p : n)
%>
type {{modelName}} struct { {{each columns c}}
    {{_.padEnd(pascal(c.columnName), maxFieldLength)}} {{_.padEnd(typeMapping(c.columnType,'go'),maxTypeLength)}} \`json:"{{camel(c.columnName)}}"\`{{_.padEnd('', maxFieldLength - pascal(c.columnName).length) }} // {{c.columnComment}} {{/each}}
}
    `.trim(),
    },
    {
      name: 'rust',
      lang: 'rust',
      code: `
use chrono::{DateTime, Local};
use serde::{Deserialize, Serialize};
<%
const modelName = pascal(tableName.split('_')[1])  // 去掉sys_前缀
%>
/// {{_.trimEnd(tableComment, '表')}}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct {{modelName}} {
    {{each columns c}}
    /// {{c.columnComment}}
    pub {{camel(c.columnName)}}: {{if c.isNonNull}}{{typeMapping(c.columnType,'rust')}}{{else}}Option<{{typeMapping(c.columnType,'rust')}}>{{/if}},
    {{/each}}
}
    `.trim()
    },
    {
      name: 'java',
      lang: 'java',
      code: `
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fofund.ark.common.annotation.Keyword;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.hibernate.validator.constraints.Length;

import java.util.List;
<%
/* 通用字段的特殊处理 */
const modelName = pascal(tableName.split('_')[1])  // 去掉sys_前缀
const createColumns = ['create_by','create_at']
const updateColumns = ['update_by','update_at'] 
const deleteFlag = 'delete_flag'
%>
/**
 * {{_.trimEnd(tableComment, '表')}}
 *
 * @author metaman {{dayjs().format('YYYY-MM-DD')}}
 */
@Data
@TableName("{{tableName}}") @Schema(description = "{{_.trimEnd(tableComment, '表')}}")
public class {{modelName}} {
   {{each columns c}}
    @Schema(description = "{{c.columnComment}}"){{if c.isPrimary}}
    @TableId(type = IdType.ASSIGN_ID){{/if}}{{if createColumns.indexOf(_.toLower(c.columnName)) > 0}}
    @TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER){{/if}}{{if updateColumns.indexOf(_.toLower(c.columnName)) > 0}}
    @TableField(fill = FieldFill.UPDATE, insertStrategy = FieldStrategy.NEVER){{/if}}{{if deleteFlag === c.columnName}}
    @TableLogic  @JsonIgnore{{/if}}
    private {{typeMapping(c.columnType,'java')}} {{camel(c.columnName)}};
   {{/each}}
}
  `.trim()
    },
    // {
    //   name: 'jsModel',
    //   lang: 'js',
    //   code: ``.trim(),
    // },
    // {
    //   name: 'javaModel',
    //   lang: 'java',
    //   code: ``.trim(),
    // },
    // {
    //   name: 'rustModel',
    //   lang: 'rust',
    //   code: ``.trim(),
    // },
    // {
    //   name: 'goModel',
    //   lang: 'go',
    //   code: ``.trim(),
    // },
  ]
}


export const defaultRender = (name, code, json, common) => {
  try {
    switch (name) {
      case "Js引擎"   : return toJsonString(new Function(code).call(json), null, 2)
      case "JsonPath" : return toJsonString(JSONPath({path: jsonPathLine(code), json: json}))
      case "Art模板"  : return artTemplate.render(code + common, json, {imports: {dayjs, _}, debug: true})
      default         : return `【${name}】暂不支持`
    }
  } catch (e) {
    return `【${name}】报错: ${e}`
  }
}

/**
 * 选择jsonPath所在行: 以$开头
 */
const jsonPathLine = str => {
  const lines = str.split('\n')
  for (let line of lines) {
    if (line.trim().startsWith('$')) return line.trim()
  }
  return '$'
}

/**
 * 格式化为json字符串
 */
const toJsonString = obj => JSON.stringify(obj, null, 2)
export const formatJson = str => toJsonString(JSON.parse(str))
export const compressJson = str => JSON.stringify(JSON.parse(str))